import { SkyboxIndices, SkyboxPosition } from "../../geom/SkyboxGeometry";
import { Matrix4 } from "../../core/math/Matrix4";
import { Vector3 } from "../../core/math/Vector3";
import { mat3, mat4 } from "../../core/math/wgpu-matrix.module";
const vertex_shader = () => {
  return /*wgsl*/`
  struct VertexOutput {
      @builtin(position) Position : vec4<f32>,
      @location(0) TexCoords : vec2<f32>,
  };
  @vertex
  fn main(@builtin(vertex_index) VertexID:u32) -> VertexOutput {
      var output : VertexOutput;
      var x = f32((VertexID & 1) << 2);
      var y = f32((VertexID & 2) << 1);
      output.TexCoords = vec2(x * 0.5, y * 0.5);
      output.Position = vec4(x - 1.0, y - 1.0, 0, 1);
      return output;
  }
  `
}


const lambertian_fragmentShader = () => {
  return /*wgsl*/`
  const M_PI =3.1415926535897932384626433832795;
  @group(0) @binding(0) var Sampler: sampler;
  @group(0) @binding(1) var u_cubemapTexture: texture_cube<f32>;
  @group(0) @binding(2) var<uniform> u_currentFace: u32;
  @group(0) @binding(3) var<uniform> u_intensityScale: f32;
  struct MicrofacetDistributionSample
  {
    pdf:f32,
    cosTheta:f32,
    sinTheta:f32,
    phi:f32
  };
  // 将 Y 轴映射到 Z 轴 ；将 Z 轴映射到 -Y； X 轴保持不变
  fn uvToXYZ(face:u32, uv:vec2<f32>) -> vec3<f32>
  {
    if(face == 0){
      return vec3( -1.0,   -uv.x,    uv.y); //+X  (x,y,z) -> (x,-z,y)
    }
    else if(face == 1){
       return vec3( 1.0,   uv.x,     uv.y); //-X  (x,y,z) -> (x,z,y)
    } 
    else if(face == 2){
      return vec3(   -uv.x,   -uv.y,    1.0); //+Y  (x,y,z) -> (x,-y,-z)
    }
    else if(face == 3){
      return vec3(   -uv.x,    uv.y,     -1.0); //-Y  (x,y,z) -> (x,y,z)
    }
    else if(face == 4){
      return vec3(   -uv.y,    1.0,  -uv.x,  ); //+Z  (x,y,z) -> (x,-z,-y)
    }
    else {//if(face == 5)
      return vec3(    uv.x,  -1.0,     uv.y);//-Z  (x,y,z) -> (-x,z,-y)
    } 
  }
  fn dirToUV(dir:vec3<f32>) -> vec2<f32>
  {
    return vec2(
            0.5 + 0.5 * atan2(dir.z, dir.x) / M_PI,
            1. - acos(dir.y) / M_PI);
  }
  fn saturate(v:f32) -> f32
  {
    return clamp(v, 0.0, 1.0);
  }
  fn radicalInverse_VdC(bits1:u32) -> f32
  {
    var bits:u32=bits1;
    bits = (bits << 16u) | (bits >> 16u);
    bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
    return f32(bits) * 2.3283064365386963e-10; // / 0x100000000
  }
  fn hammersley2d(i:u32, N:u32) -> vec2<f32> {
    return vec2(f32(i)/f32(N), radicalInverse_VdC(u32(i)));
  }
  fn generateTBN(normal:vec3<f32>) -> mat3x3<f32>
  {
    var bitangent:vec3<f32> = vec3(0.0, 1.0, 0.0);

    var NdotUp = dot(normal, vec3(0.0, 1.0, 0.0));
    var epsilon = 0.0000001;
    if (1.0 - abs(NdotUp) <= epsilon)
    {
        if (NdotUp > 0.0)
        {
            bitangent = vec3(0.0, 0.0, 1.0);
        }
        else
        {
            bitangent = vec3(0.0, 0.0, -1.0);
        }
    }

    var tangent:vec3<f32> = normalize(cross(bitangent, normal));
    bitangent = cross(normal, tangent);

    return mat3x3(tangent, bitangent, normal);
 }
  fn Lambertian(xi:vec2<f32>, roughness:f32) -> MicrofacetDistributionSample
  {
    var lambertian:MicrofacetDistributionSample;
    lambertian.cosTheta = sqrt(1.0 - xi.y);
    lambertian.sinTheta = sqrt(xi.y); 
    lambertian.phi = 2.0 * M_PI * xi.x;

    lambertian.pdf = lambertian.cosTheta / M_PI; 

    return lambertian;
 }
  fn getImportanceSample(sampleIndex:u32, N:vec3<f32>, roughness:f32) -> vec4<f32>
  {
      var xi = hammersley2d(sampleIndex, 2048);
      var importanceSample = Lambertian(xi, roughness);
      var localSpaceDirection = normalize(vec3(
          importanceSample.sinTheta * cos(importanceSample.phi), 
          importanceSample.sinTheta * sin(importanceSample.phi), 
          importanceSample.cosTheta
      ));
      var TBN = generateTBN(N);
      var direction = TBN * localSpaceDirection;
  
      return vec4(direction, importanceSample.pdf);
  }
  fn computeLod(pdf:f32) -> f32
  {
    var lod = 0.5 * log2( 6.0 * 256.0 * 256.0 / ( f32(2048) * pdf));
    return lod;
  }
  @fragment
  fn main(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> 
  {       
      var color = vec3<f32>(0);
      var newUV = texCoord ;
      newUV = newUV*2.0-1.0;
      var scan = uvToXYZ(u_currentFace, newUV);
      var direction = normalize(scan);
      direction.y = -direction.y;
      var N = direction;
      var weight = 0.0;
      for(var i:u32 = 0; i < 2048; i++)
      {
          var importanceSample = getImportanceSample(i, N, 0.0);
          var H = vec3(importanceSample.xyz);
          var pdf = importanceSample.w;
          var lod = computeLod(pdf);
          lod += 0;
          var lambertian = textureSampleLevel(u_cubemapTexture,Sampler, H, lod).rgb * u_intensityScale;
          color += lambertian;
      }
  
      if(weight != 0.0f)
      {
          color /= weight;
      }
      else
      {
          color /= f32(2048);
      }
      return vec4(color, 1.0);
  }
  `
}
const Specular_fragmentShader = () => {
  return /*wgsl*/`
  const M_PI =3.1415926535897932384626433832795;
  @group(0) @binding(0) var Sampler: sampler;
  @group(0) @binding(1) var u_cubemapTexture: texture_cube<f32>;
  @group(0) @binding(2) var<uniform> u_currentFace: u32;
  @group(0) @binding(3) var<uniform> u_intensityScale: f32;
  @group(0) @binding(4) var<uniform> u_roughness: f32;
  struct MicrofacetDistributionSample
  {
    pdf:f32,
    cosTheta:f32,
    sinTheta:f32,
    phi:f32
  };
  // 将 Y 轴映射到 Z 轴 ；将 Z 轴映射到 -Y； X 轴保持不变
  fn uvToXYZ(face:u32, uv:vec2<f32>) -> vec3<f32>
  {
    if(face == 0){
      return vec3( -1.0,   -uv.x,    uv.y); //+X  (x,y,z) -> (x,-z,y)
    }
    else if(face == 1){
       return vec3( 1.0,   uv.x,     uv.y); //-X  (x,y,z) -> (x,z,y)
    } 
    else if(face == 2){
      return vec3(   -uv.x,   -uv.y,    1.0); //+Y  (x,y,z) -> (x,-y,-z)
    }
    else if(face == 3){
      return vec3(   -uv.x,    uv.y,     -1.0); //-Y  (x,y,z) -> (x,y,z)
    }
    else if(face == 4){
      return vec3(   -uv.y,    1.0,  -uv.x,  ); //+Z  (x,y,z) -> (x,-z,-y)
    }
    else {//if(face == 5)
      return vec3(    uv.x,  -1.0,     uv.y);//-Z  (x,y,z) -> (-x,z,-y)
    } 
  }
  fn dirToUV(dir:vec3<f32>) -> vec2<f32>
  {
    return vec2(
            0.5f + 0.5f * atan2(dir.z, dir.x) / M_PI,
            1.f - acos(dir.y) / M_PI);
  }
  fn saturate(v:f32) -> f32
  {
    return clamp(v, 0.0, 1.0);
  }
  fn radicalInverse_VdC(bits1:u32) -> f32
  {
    var bits:u32=bits1;
    bits = (bits << 16u) | (bits >> 16u);
    bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
    return f32(bits) * 2.3283064365386963e-10; // / 0x100000000
  }
  fn hammersley2d(i:u32, N:u32) -> vec2<f32> {
    return vec2(f32(i)/f32(N), radicalInverse_VdC(u32(i)));
  }
  fn generateTBN(normal:vec3<f32>) -> mat3x3<f32>
  {
    var bitangent:vec3<f32> = vec3(0.0, 1.0, 0.0);

    var NdotUp = dot(normal, vec3(0.0, 1.0, 0.0));
    var epsilon = 0.0000001;
    if (1.0 - abs(NdotUp) <= epsilon)
    {
        if (NdotUp > 0.0)
        {
            bitangent = vec3(0.0, 0.0, 1.0);
        }
        else
        {
            bitangent = vec3(0.0, 0.0, -1.0);
        }
    }

    var tangent:vec3<f32> = normalize(cross(bitangent, normal));
    bitangent = cross(normal, tangent);

    return mat3x3(tangent, bitangent, normal);
 }
 fn D_GGX(NdotH:f32, roughness:f32) -> f32 {
  var a = NdotH * roughness;
  var k = roughness / (1.0 - NdotH * NdotH + a * a);
  return k * k * (1.0 / M_PI);
}
 fn GGX(xi:vec2<f32>, roughness:f32) -> MicrofacetDistributionSample
 {
     var ggx:MicrofacetDistributionSample;
 
     var alpha = roughness * roughness;
     ggx.cosTheta = saturate(sqrt((1.0 - xi.y) / (1.0 + (alpha * alpha - 1.0) * xi.y)));
     ggx.sinTheta = sqrt(1.0 - ggx.cosTheta * ggx.cosTheta);
     ggx.phi = 2.0 * M_PI * xi.x;
 
     ggx.pdf = D_GGX(ggx.cosTheta, alpha);
 
     ggx.pdf /= 4.0;
 
     return ggx;
 }
  fn getImportanceSample(sampleIndex:u32, N:vec3<f32>, roughness:f32) -> vec4<f32>
  {
      var xi = hammersley2d(sampleIndex, 1024);
      var importanceSample:MicrofacetDistributionSample = GGX(xi, roughness);
      var localSpaceDirection = normalize(vec3(
          importanceSample.sinTheta * cos(importanceSample.phi), 
          importanceSample.sinTheta * sin(importanceSample.phi), 
          importanceSample.cosTheta
      ));
      var TBN = generateTBN(N);
      var direction = TBN * localSpaceDirection;
  
      return vec4(direction, importanceSample.pdf);
  }
  fn computeLod(pdf:f32) -> f32
  {
    var lod = 0.5 * log2( 6.0 * 256.0 * 256.0 / ( f32(2048) * pdf));
    return lod;
  }
  @fragment
  fn main(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> 
  {       
      var color = vec3<f32>(0);
      var newUV = texCoord ;
      newUV = newUV*2.0-1.0;
      var scan = uvToXYZ(u_currentFace, newUV);
      var direction = normalize(scan);
      direction.y = -direction.y;
      var N = direction;
      var weight = 0.0;
      for(var i:u32 = 0; i < 1024; i++)
      {
          var importanceSample = getImportanceSample(i, N, u_roughness);
          var H = vec3(importanceSample.xyz);
          var pdf = importanceSample.w;
          var lod = computeLod(pdf);
          lod += 0.0;
          var V = N;
          var L = normalize(reflect(-V, H));
          var NdotL = dot(N, L);
          if (NdotL > 0.0)
            {
                if(u_roughness == 0.0)
                {
                    lod = 0.0;
                }
                var sampleColor = textureSampleLevel(u_cubemapTexture,Sampler, L, lod).rgb * u_intensityScale;
                color += sampleColor * NdotL;
                weight += NdotL;
            }
      }
  
      if(weight != 0.0f)
      {
          color /= weight;
      }
      else
      {
          color /= f32(1024);
      }
      return vec4(color, 1.0);
  }`
}
const lut_fragmentShader = () => {
  return /*wgsl*/`
  const M_PI =3.1415926535897932384626433832795;
  struct MicrofacetDistributionSample
  {
    pdf:f32,
    cosTheta:f32,
    sinTheta:f32,
    phi:f32
  };
  // 将 Y 轴映射到 Z 轴 ；将 Z 轴映射到 -Y； X 轴保持不变
  fn uvToXYZ(face:u32, uv:vec2<f32>) -> vec3<f32>
  {
    if(face == 0){
      return vec3( -1.0,   -uv.x,    uv.y); //+X  (x,y,z) -> (x,-z,y)
    }
    else if(face == 1){
       return vec3( 1.0,   uv.x,     uv.y); //-X  (x,y,z) -> (x,z,y)
    } 
    else if(face == 2){
      return vec3(   -uv.x,   -uv.y,    1.0); //+Y  (x,y,z) -> (x,-y,-z)
    }
    else if(face == 3){
      return vec3(   -uv.x,    uv.y,     -1.0); //-Y  (x,y,z) -> (x,y,z)
    }
    else if(face == 4){
      return vec3(   -uv.y,    1.0,  -uv.x,  ); //+Z  (x,y,z) -> (x,-z,-y)
    }
    else {//if(face == 5)
      return vec3(    uv.x,  -1.0,     uv.y);//-Z  (x,y,z) -> (-x,z,-y)
    } 
  }
  fn dirToUV(dir:vec3<f32>) -> vec2<f32>
  {
    return vec2(
            0.5f + 0.5f * atan2(dir.z, dir.x) / M_PI,
            1.f - acos(dir.y) / M_PI);
  }
  fn saturate(v:f32) -> f32
  {
    return clamp(v, 0.0, 1.0);
  }
  fn radicalInverse_VdC(bits1:u32) -> f32
  {
    var bits:u32=bits1;
    bits = (bits << 16u) | (bits >> 16u);
    bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
    return f32(bits) * 2.3283064365386963e-10; // / 0x100000000
  }
  fn hammersley2d(i:u32, N:u32) -> vec2<f32> {
    return vec2(f32(i)/f32(N), radicalInverse_VdC(u32(i)));
  }
  fn generateTBN(normal:vec3<f32>) -> mat3x3<f32>
  {
    var bitangent:vec3<f32> = vec3(0.0, 1.0, 0.0);

    var NdotUp = dot(normal, vec3(0.0, 1.0, 0.0));
    var epsilon = 0.0000001;
    if (1.0 - abs(NdotUp) <= epsilon)
    {
        if (NdotUp > 0.0)
        {
            bitangent = vec3(0.0, 0.0, 1.0);
        }
        else
        {
            bitangent = vec3(0.0, 0.0, -1.0);
        }
    }

    var tangent:vec3<f32> = normalize(cross(bitangent, normal));
    bitangent = cross(normal, tangent);

    return mat3x3(tangent, bitangent, normal);
 }
 fn D_GGX(NdotH:f32, roughness:f32) -> f32 {
  var a = NdotH * roughness;
  var k = roughness / (1.0 - NdotH * NdotH + a * a);
  return k * k * (1.0 / M_PI);
}
 fn GGX(xi:vec2<f32>, roughness:f32) -> MicrofacetDistributionSample
 {
     var ggx:MicrofacetDistributionSample;
 
     var alpha = roughness * roughness;
     ggx.cosTheta = saturate(sqrt((1.0 - xi.y) / (1.0 + (alpha * alpha - 1.0) * xi.y)));
     ggx.sinTheta = sqrt(1.0 - ggx.cosTheta * ggx.cosTheta);
     ggx.phi = 2.0 * M_PI * xi.x;
 
     ggx.pdf = D_GGX(ggx.cosTheta, alpha);
 
     ggx.pdf /= 4.0;
 
     return ggx;
 }
  fn getImportanceSample(sampleIndex:u32, N:vec3<f32>, roughness:f32) -> vec4<f32>
  {
      var xi = hammersley2d(sampleIndex, 1024);
      var importanceSample:MicrofacetDistributionSample = GGX(xi, roughness);
      var localSpaceDirection = normalize(vec3(
          importanceSample.sinTheta * cos(importanceSample.phi), 
          importanceSample.sinTheta * sin(importanceSample.phi), 
          importanceSample.cosTheta
      ));
      var TBN = generateTBN(N);
      var direction = TBN * localSpaceDirection;
  
      return vec4(direction, importanceSample.pdf);
  }
  fn computeLod(pdf:f32) -> f32
  {
    var lod = 0.5 * log2( 6.0 * 256.0 * 256.0 / ( f32(2048) * pdf));
    return lod;
  }
  fn V_SmithGGXCorrelated(NoV:f32, NoL:f32, roughness:f32) -> f32 {
    var a2 = pow(roughness, 4.0);
    var GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
    var GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
    return 0.5 / (GGXV + GGXL);
}
  @fragment
  fn main(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> 
  {       
      var color = vec3<f32>(0);
      var NdotV = texCoord.x;
      var roughness = texCoord.y;
      var V = vec3(sqrt(1.0 - NdotV * NdotV), 0.0, NdotV);
      var N = vec3(0.0, 0.0, 1.0);
      var A = 0.0;
      var B = 0.0;
      var C = 0.0;

      for(var i:u32 = 0; i < 512; i++)
      {
          var importanceSample = getImportanceSample(i, N, roughness);
          var H = vec3(importanceSample.xyz);
          var L = normalize(reflect(-V, H));
          var NdotL = saturate(L.z);
          var NdotH = saturate(H.z);
          var VdotH = saturate(dot(V, H));
          if (NdotL > 0.0)
          {
            var V_pdf = V_SmithGGXCorrelated(NdotV, NdotL, roughness) * VdotH * NdotL / NdotH;
            var Fc = pow(1.0 - VdotH, 5.0);
            A += (1.0 - Fc) * V_pdf;
            B += Fc * V_pdf;
            C += 0.0;
                
          }
          
      }
      color=vec3(4.0 * A, 4.0 * B, 4.0 * 2.0 * M_PI * C) / f32(512);
      return vec4(color, 1.0);
  }`
}


const generateIBL = async (cubeMapTexture, device, format, context) => {
  const textureSize = 256
  const ggxSampleCount = 1024
  const lambertianSampleCount = 2048
  const mipmapLevels = 5
  const lutResolution = 1024
  // lambertian
  const sampler = device.createSampler({
    magFilter: 'linear',
    minFilter: 'linear'
  });
  // 辐照度贴图
  const pipelineIrradiance = await device.createRenderPipelineAsync({
    label: 'pipeline-lambertian',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: vertex_shader()
      }),
      entryPoint: 'main',
    },
    fragment: {
      module: device.createShaderModule({
        code: lambertian_fragmentShader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    },
  });
  const cubeMapIrradiance = device.createTexture({
    dimension: '2d',
    size: [textureSize, textureSize, 6],
    format: format,
    usage:
      GPUTextureUsage.TEXTURE_BINDING |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.RENDER_ATTACHMENT
  });
  const u_currentFaceBuffer = device.createBuffer({
    label: 'u_currentFaceBuffer',
    size: 4, // 4 x 4 x float32
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  const u_intensityScaleBuffer = device.createBuffer({
    label: 'u_intensityScaleBuffer',
    size: 4, // 4 x 4 x float32
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  device.queue.writeBuffer(u_intensityScaleBuffer, 0, new Float32Array([1]));
  const fragmentBindGroupIrradiance = device.createBindGroup({
    label: 'fragmentBindGroupIrradiance',
    layout: pipelineIrradiance.getBindGroupLayout(0),
    entries: [
      {
        binding: 0,
        resource: sampler
      },
      {
        binding: 1,
        resource: cubeMapTexture.createView({
          dimension: 'cube'
        })
      },
      {
        binding: 2,
        resource: {
          buffer: u_currentFaceBuffer,
          offset: 0,
          size: 4
        }
      },
      {
        binding: 3,
        resource: {
          buffer: u_intensityScaleBuffer,
          offset: 0,
          size: 4
        }
      }
    ]
  });
  // 为renderPass创建depthTexture
  const depthTextureIrradiance = device.createTexture({
    label: 'depthTextureIrradiance',
    size: [textureSize, textureSize],
    format: 'depth24plus',
    usage: GPUTextureUsage.RENDER_ATTACHMENT
  });
  const depthViewIrradiance = depthTextureIrradiance.createView();
  const renderTextureIrradiance = device.createTexture({
    label: 'renderTextureIrradiance',
    size: [textureSize, textureSize],
    format: format,
    sampleCount: 1,
    usage:
      GPUTextureUsage.RENDER_ATTACHMENT |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC
  });
  const renderViewIrradiance = renderTextureIrradiance.createView();
  const renderPassDescriptorIrradiance = {
    colorAttachments: [
      {
        view: renderViewIrradiance,
        clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store'
      }
    ],
    depthStencilAttachment: {
      view: depthViewIrradiance,
      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store'
    }
  };
  for (var i = 0; i < 6; ++i) {
    const commandEncoder = device.createCommandEncoder();
    const passEncoderIrradiance = commandEncoder.beginRenderPass(
      renderPassDescriptorIrradiance
    );
    passEncoderIrradiance.setPipeline(pipelineIrradiance);
    let currentFace = new Uint32Array([i])
    device.queue.writeBuffer(u_currentFaceBuffer, 0, currentFace);

    passEncoderIrradiance.setBindGroup(0, fragmentBindGroupIrradiance);
    passEncoderIrradiance.draw(3, 1, 0, 0);
    passEncoderIrradiance.end();
    commandEncoder.copyTextureToTexture(
      {
        texture: renderTextureIrradiance
      },
      {
        texture: cubeMapIrradiance,
        origin: [0, 0, i]
      },
      [textureSize, textureSize]
    );
    device.queue.submit([commandEncoder.finish()]);
  }
  // specular
  const pipelinePrefilter = await device.createRenderPipelineAsync({
    label: 'pipelinePrefilter',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: vertex_shader()
      }),
      entryPoint: 'main',
    },
    fragment: {
      module: device.createShaderModule({
        code: Specular_fragmentShader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    }
  });
  const u_currentFaceBufferSpecular = device.createBuffer({
    label: 'u_currentFaceBuffer',
    size: 4, // u32 大小为 4 字节
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });

  const u_intensityScaleBufferSpecular = device.createBuffer({
    label: 'u_intensityScaleBuffer',
    size: 4, // f32 大小为 4 字节
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  device.queue.writeBuffer(u_intensityScaleBufferSpecular, 0, new Float32Array([1]));
  const u_roughnessBuffer = device.createBuffer({
    label: 'u_roughnessBuffer',
    size: 4, // f32 大小为 4 字节 
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });

  const fragmentBindGroupPrefilter = device.createBindGroup({
    label: 'fragmentBindGroupPrefilter',
    layout: pipelinePrefilter.getBindGroupLayout(0),
    entries: [
      {
        binding: 0,
        resource: sampler
      },
      {
        binding: 1,
        resource: cubeMapTexture.createView({
          dimension: 'cube'
        })
      },
      {
        binding: 2,
        resource: {
          buffer: u_currentFaceBufferSpecular,
          offset: 0,
          size: 4
        }
      },
      {
        binding: 3,
        resource: {
          buffer: u_intensityScaleBufferSpecular,
          offset: 0,
          size: 4
        }
      },
      {
        binding: 4,
        resource: {
          buffer: u_roughnessBuffer,
          offset: 0,
          size: 4
        }
      }
    ]
  });
  const cubeMapPrefilter = device.createTexture({
    label: 'cubeMapPrefilter',
    dimension: '2d',
    size: [textureSize, textureSize, 6],
    format: format,
    mipLevelCount: 5,
    usage:
      GPUTextureUsage.TEXTURE_BINDING |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.RENDER_ATTACHMENT
  });



  for (var currentMipLevel = 0; currentMipLevel < mipmapLevels; ++currentMipLevel) {
    const roughness = (currentMipLevel) / (mipmapLevels - 1);
    const currentTextureSize = textureSize >> currentMipLevel;
    device.queue.writeBuffer(
      u_roughnessBuffer,
      0,
      new Float32Array([roughness])
    );
    const depthTexturePrefilter = device.createTexture({
      label: 'depthTexturePrefilter',
      size: [currentTextureSize, currentTextureSize],
      format: 'depth24plus',
      usage: GPUTextureUsage.RENDER_ATTACHMENT
    });
    const depthViewPrefilter = depthTexturePrefilter.createView();
    const renderTexturePrefilter = device.createTexture({
      label: 'renderTexturePrefilter',
      size: [currentTextureSize, currentTextureSize],
      format: format,
      sampleCount: 1,
      usage:
        GPUTextureUsage.RENDER_ATTACHMENT |
        GPUTextureUsage.COPY_SRC |
        GPUTextureUsage.TEXTURE_BINDING
    });

    const renderPassDescriptorPrefilter = {
      colorAttachments: [
        {
          view: renderTexturePrefilter.createView(),
          clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store'
        }
      ],
      depthStencilAttachment: {
        view: depthViewPrefilter,
        depthClearValue: 1.0,
        depthLoadOp: 'clear',
        depthStoreOp: 'store'
      }
    };

    for (var i = 0; i < 6; ++i) {
      const commandEncoder = device.createCommandEncoder({});
      const passEncoderPrefilter = commandEncoder.beginRenderPass(
        renderPassDescriptorPrefilter
      );
      let currentFace = new Uint32Array([i])
      device.queue.writeBuffer(u_currentFaceBufferSpecular, 0, currentFace);
      passEncoderPrefilter.setPipeline(pipelinePrefilter);
      passEncoderPrefilter.setBindGroup(0, fragmentBindGroupPrefilter);
      passEncoderPrefilter.draw(3, 1, 0, 0);
      passEncoderPrefilter.end();
      const mipLevelSize = {
        width: currentTextureSize,
        height: currentTextureSize
      };
      commandEncoder.copyTextureToTexture(
        {
          texture: renderTexturePrefilter,
          mipLevel: 0
        },
        {
          texture: cubeMapPrefilter,
          origin: [0, 0, i],
          mipLevel: currentMipLevel
        },
        mipLevelSize
      );

      device.queue.submit([commandEncoder.finish()]);
    }
  }
  // BrdfLut
  // var img = new Image();
  // img.src = lutBase;
  // await img.decode();
  // let imageLut = await createImageBitmap(img);
  // const renderTextureLut = engine.device.createTexture({
  //   dimension: '2d',
  //   size: [imageLut.width, imageLut.height],
  //   format: format,
  //   usage:
  //     GPUTextureUsage.TEXTURE_BINDING |
  //     GPUTextureUsage.COPY_DST |
  //     GPUTextureUsage.RENDER_ATTACHMENT
  // });
  // engine.device.queue.copyExternalImageToTexture(
  //   { source: imageLut },
  //   { texture: renderTextureLut },
  //   [imageLut.width, imageLut.height]
  // );
  const pipelinelut = await device.createRenderPipelineAsync({
    label: 'pipelinelut',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: vertex_shader()
      }),
      entryPoint: 'main',
    },
    fragment: {
      module: device.createShaderModule({
        code: lut_fragmentShader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    }
  });
  const depthTextureLut = device.createTexture({
    size: [lutResolution, lutResolution],
    format: 'depth24plus',
    usage: GPUTextureUsage.RENDER_ATTACHMENT
  });
  const renderTextureLut = device.createTexture({
    size: [lutResolution, lutResolution],
    format: format,
    sampleCount: 1,
    usage:
      GPUTextureUsage.RENDER_ATTACHMENT |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.TEXTURE_BINDING
  });
  const renderPassDescriptorlut = {
    colorAttachments: [
      {
        view: renderTextureLut.createView(),
        clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store'
      }
    ],
    depthStencilAttachment: {
      view: depthTextureLut.createView(),
      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store'
    }
  };
  // renderPassDescriptorlut.colorAttachments[0].view=context.getCurrentTexture().createView()
  const commandEncoder = device.createCommandEncoder({});
  const passEncoderlut = commandEncoder.beginRenderPass(
    renderPassDescriptorlut
  );
  passEncoderlut.setPipeline(pipelinelut);
  passEncoderlut.draw(3, 1, 0, 0);
  passEncoderlut.end();
  device.queue.submit([commandEncoder.finish()]);
  return { cubeMapIrradiance, cubeMapPrefilter, renderTextureLut }
}
export { generateIBL }